#include "SimpleCore.hpp"

//Abilita l'esecuzione di codice di debug
//#define __SIMPLECORE_DEBUG__
//File entro il quale verranno scritti i tempi di esecuzione di ogni generazione
#define __SC_GENTIME_FILE__     "sc_Gtime.txt"
#define __SC_DOT_PRINT_INTERVAL__	50

SimpleCore::SimpleCore(Validator *val,GAmodSettings &settings) throw(SimpleCoreException)
: GAcore(val,settings)
{
    M = N = NULL;

    this->numCrossover=0;
    this->numMut = 0;

    try
    {
        this->loadSettings(settings,GAcore::THROW_EXCEPTION);
    }
    catch(SimpleCoreException &se)
    {
        std::string errmsg("SimpleCore::SimpleCore:\n\t");
        errmsg = errmsg + se.msg;
        throw SimpleCoreException((char*)errmsg.c_str());
    }

    if(!M)
        throw SimpleCoreException("SimpleCore::SimpleCore: Nessuna popolazione specificata nella configurazione");
}

int SimpleCore::loadSettings(GAmodSettings &settings,load_exc le) throw(SimpleCoreException)
{

	TemplateGAmodObjWrapper<Popolazione*> *tgw;
	Popolazione *pop;
    int p_size=0,np=0,init_pop=1;
    TemplateGAmodObjWrapper<string> *strsett;
    const char *tmp;


	tgw = (TemplateGAmodObjWrapper<Popolazione*>*)settings.get_setting("pop");
	pop = (tgw) ? tgw->value : NULL;

	if(pop)
	{
		IF_DBG_INFO(printf("[INFO] SimpleCore::loadSettings: Specificata popolazione (%d)[%d,%d]\n",pop->get_size(),pop->pop_rows(),pop->pop_cols());)
		M = pop->copy();
		N = new Popolazione(pop->get_size(),pop->pop_rows(),pop->pop_cols());
		init_pop = 0;
		np++;
	}

    strsett = (TemplateGAmodObjWrapper<string>*)settings.get_setting("pop_size");
    tmp = (strsett) ? strsett->value.c_str() : NULL;

    if(tmp)
    {
        p_size = atoi(tmp);
        //free((void*)tmp);
        IF_DBG_VERB(printf("[INFO] SimpleCore::loadSettings: POP_SIZE <%s> = %d\n",tmp,p_size);)

        if(p_size<=0)
            throw SimpleCoreException("SimpleCore::loadSettings: Dimensione errata della popolazione");

        if(M)
			M->resize(p_size);
		else
			M = new Popolazione(p_size);

        if(N)
			N->resize(p_size);
		else
			N = new Popolazione(p_size);

        M->randomInit();
        np++;
    }

	if(init_pop)
	{
		strsett = (TemplateGAmodObjWrapper<string>*)settings.get_setting("pop_file");
		tmp = (strsett) ? strsett->value.c_str() : NULL;

		if(tmp)
		{
			IF_DBG_VERB(printf("[INFO] SimpleCore::loadSettings: POP_FILE <%s> ",tmp);)
			if(M)	//Nel file di configurazione e' gia' stata specificata una dimensione di popolazione
			{
				try
				{
					M->load_from_file((char*)tmp,Popolazione::LOAD_RES_POLICY_NORESIZE);
					IF_DBG_VERB(printf("loaded.\n");)
				}
				catch(PopolazioneException &pe)
				{
					std::string errmsg("SimpleCore::loadSettings:\n\t");
					errmsg = errmsg + pe.msg;
					throw SimpleCoreException((char*)errmsg.c_str());
				}
			}
			else
			{
				try
				{
					M = new Popolazione((char*)tmp);
					N = new Popolazione(M->get_size());
				}
				catch(PopolazioneException &e)
				{
					std::string errmsg("SimpleCore::loadSettings:\n\t");
					errmsg = errmsg + e.msg;
					throw SimpleCoreException((char*)errmsg.c_str());
				}
			}
			//free((void*)tmp);
			np++;
		}
	}

	if(np==0)
		throw SimpleCoreException("SimpleCore::loadSettings: specificare POP_FILE, POP_SIZE o passare una popolazione preallocata");

    strsett = (TemplateGAmodObjWrapper<string>*)settings.get_setting("pt");
    tmp = (strsett) ? strsett->value.c_str() : NULL;

    if(!tmp && le == GAcore::THROW_EXCEPTION)
        throw SimpleCoreException("SimpleCore::loadSettings: Pt non specificata");
    else if(tmp)
    {
        this->pt = atof(tmp);
        IF_DBG_VERB(printf("[INFO] SimpleCore::loadSettings: pt <%s> = %f\n",tmp,this->pt);)

        if(this->pt<=0 || this->pt>1)
            throw SimpleCoreException("SimpleCore::loadSettings: valore errato di pt");

        np++;
    }
    //-----------------------------


    strsett = (TemplateGAmodObjWrapper<string>*)settings.get_setting("pc");
    tmp = (strsett) ? strsett->value.c_str() : NULL;

    if(!tmp && le == GAcore::THROW_EXCEPTION)
        throw SimpleCoreException("SimpleCore::loadSettings: Pc non specificata");
    else if(tmp)
    {
        this->pc = atof(tmp);
        IF_DBG_VERB(printf("[INFO] SimpleCore::loadSettings: PC <%s> = %f\n",tmp,this->pc);)

        if(this->pc<=0 || this->pc>1)
            throw SimpleCoreException("SimpleCore::loadSettings: valore errato di pc");

        np++;
    }
    //-----------------------------
    strsett = (TemplateGAmodObjWrapper<string>*)settings.get_setting("pm");
    tmp = (strsett) ? strsett->value.c_str() : NULL;

    if(!tmp && le == GAcore::THROW_EXCEPTION)
        throw SimpleCoreException("SimpleCore::loadSettings: Pm non specificata");
    else if(tmp)
    {
        this->pm = atof(tmp);
        IF_DBG_VERB(printf("[INFO] SimpleCore::loadSettings: pm <%s> = %f\n",tmp,this->pm);)

        if(this->pm<=0 || this->pm>1)
            throw SimpleCoreException("SimpleCore::loadSettings: valore errato di pm");

        np++;
    }
    //-----------------------------
    strsett = (TemplateGAmodObjWrapper<string>*)settings.get_setting("t_size");
    tmp = (strsett) ? strsett->value.c_str() : NULL;

    if(!tmp && le == GAcore::THROW_EXCEPTION)
        throw SimpleCoreException("SimpleCore::loadSettings: t_size non specificato");
    else if(tmp)
    {
        this->t_size = atoi(tmp);
        IF_DBG_VERB(printf("[INFO] SimpleCore::loadSettings: t_size <%s> = %d\n",tmp,this->t_size);)

        if(this->t_size<=0 || this->t_size > M->get_size())
            throw SimpleCoreException("SimpleCore::loadSettings: valore errato di t_size");

        if(this->t_size > MAX_TS)
        {
            IF_DBG_INFO(printf("[WARN] SimpleCore::loadSettings: valore di t_size ridotto al massimo consentito[%d]\n",MAX_TS);)
            this->t_size = MAX_TS;
        }

        np++;
    }

    //-----------------------------
    strsett = (TemplateGAmodObjWrapper<string>*)settings.get_setting("random_ind");
    tmp = (strsett) ? strsett->value.c_str() : NULL;

    if(tmp)
    {
        this->random_ind = atoi(tmp);
        IF_DBG_VERB(printf("[INFO] SimpleCore::loadSettings: random_ind <%s> = %d\n",tmp,this->random_ind);)

        if(this->max_rate<0)
            throw SimpleCoreException("SimpleCore::loadSettings: valore errato di random_ind");

        np++;
    }
    else this->random_ind = 0;

    return np;
}

/*
    best_score
    mean_score
    var_score
    n_mut
    n_cross
*/
int SimpleCore::getGenerationData(GAmodSettings &settings,HERAuint32 lista)
{
    //char str[1024];
    int np=0;
    float a;

    settings.clear();
    GAmodObjWrapper *objval;
    if(lista==0) lista = ~lista;

    if(lista & SimpleCore::PARAM_BEST_SCORE)
    {
        Individuo b;
        b = M->getBestIndividuo();
        //sprintf(str,"%d",b.fitness);
        objval = new GAmodFloatSetting((float)b.fitness);
        settings.add_setting("best_score",objval);
        np++;
    }

    if(lista & SimpleCore::PARAM_MEAN_SCORE || lista & SimpleCore::PARAM_VAR_SCORE)
    {
        //sprintf(str,"%.4f",M->mean_fitness(&a));
        objval = new GAmodFloatSetting(M->mean_fitness(&a));
        settings.add_setting("mean_score",objval);
        //sprintf(str,"%.4f",a);
        objval = new GAmodFloatSetting(a);
        settings.add_setting("var_score",objval);
        np++;
    }

    if(lista & SimpleCore::PARAM_N_MUT)
    {
        //sprintf(str,"%.4f",this->numMut);
        objval = new GAmodFloatSetting((float)this->numMut);
        settings.add_setting("n_mut",objval);
        np++;
    }

    if(lista & SimpleCore::PARAM_N_CROSS)
    {
        //sprintf(str,"%.4f",this->numCrossover);
        objval = new GAmodFloatSetting(this->numCrossover);
        settings.add_setting("n_cross",objval);
        np++;
    }

    if(lista & SimpleCore::PARAM_HAM_BEST)
    {
        //sprintf(str,"%.4f",this->M->hamming_distance(Popolazione::HAMMING_TYPE_BESTMED));
        objval = new GAmodFloatSetting(this->M->hamming_distance(Popolazione::HAMMING_TYPE_BESTMED));
        settings.add_setting("ham_best",objval);
        np++;
    }

    return np;
}

int SimpleCore::runGeneration(int num)
{
    int n,i;
#ifdef __SIMPLECORE_DEBUG__
    static FILE *sc_tdbg_file=NULL;
    clock_t t0,t1;

    if(sc_tdbg_file==NULL) sc_tdbg_file=fopen(__SC_GENTIME_FILE__,"w");
#endif
	//M->print_all();
    this->myValidator->valuta_pop(M);

    this->numCrossover = 0;
    this->numMut = 0;

    for(n=0,i=0; n<num; n++, i++)
    {
        IF_DBG_INFO(if( i %__SC_DOT_PRINT_INTERVAL__ == 0) printf("."); fflush(stdout);)

#ifdef __SIMPLECORE_DEBUG__
        t0=clock();
#endif

		try {
			//printf("SELEZIONE\n");
					//M->print_all();
					M->selection_tournament_elitism((*N),this->t_size,this->pt);
					//N->print_all();
					//printf("OK.MUTAZIONE\n");
					this->numCrossover += N->crossover((*M),this->pc,Popolazione::CROSS_OPT_P_ELIT);
					this->numMut += M->mutazione_act_adapt(this->min_rate,this->max_rate,this->myValidator->getMaxScore(),this->pm,Popolazione::MUT_OPT_P_ELIT);
					//printf("OK.CROSSOVER\n");

					//printf("OK.VALUTAZIONE");
					this->myValidator->valuta_pop(M);
		}
		catch (EHW_Exception &e) {
			printf("[ERROR] %s\n",e.msg.c_str());
			exit(1);
		}
		//printf("OK\n");
        /*if(this->random_ind>0)
        {
            M->sort_worse_individual(this->random_ind);
            for(int j=0; j<this->random_ind; j++)
                (*M)[M->get_size()-j-1].randomInit();

            this->myValidator->valuta_pop(M);
        }*/

#ifdef __SIMPLECORE_DEBUG__
        t1=clock();
        fprintf(sc_tdbg_file,"%f\n",(t1-t0)/(float)(CLOCKS_PER_SEC));
        fflush(sc_tdbg_file);
#endif

    }

    this->numCrossover = this->numCrossover / (float)num;
    this->numMut = this->numMut / (num * M->get_size());

    //printf("NC = %f, NM = %f\n",numCrossover,numMut);
    //Individuo b;
    //b = M->getBestIndividuo();
    //b.create_dxf_rp("best.dxf");
	//printf("best = %d",b.fitness);

	//M->print_all();

    return i++;
}
